package com.shopping.cart.service;

import com.shopping.cart.dao.LoadProductInformation;
import com.shopping.cart.entity.Coupon;
import com.shopping.cart.entity.Product;
import com.shopping.cart.entity.PromotionMessage;
import com.shopping.cart.entity.ShoppingList;
import com.shopping.cart.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @author qxf101
 * @since 2021-05-10
 */
public class Shopping {
    private static final Logger LOGGER = LoggerFactory.getLogger(Shopping.class);
    private static final List<Product> PRODUCTS;

    static {
        // 读取商品类型信息
        PRODUCTS = LoadProductInformation.loadDataFromJson();
    }

    /**
     * 从文件加载购物车信息
     *
     * @param fileName 文件名称
     * @return 购物车信息
     */
    private static String loadShoppingInfo(String fileName) {
        InputStream inputStream = Shopping.class.getClassLoader().getResourceAsStream(fileName + ".txt");
        if (inputStream == null) {
            return "";
        }

        StringBuilder sb = new StringBuilder();
        try (InputStreamReader input = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
             BufferedReader br = new BufferedReader(input)) {
            String line;
            while ((line = br.readLine()) != null) {
                if (!"".equals(line)) {
                    sb.append(line);
                }
                sb.append("\n");
            }
        } catch (IOException e) {
            LOGGER.error("load file has error,the file name is " + fileName);
        }

        return sb.toString();
    }

    public static float buy(String fileName) {
        // 加载商品数据信息
        String shoppingInfo = loadShoppingInfo(fileName);
        if (shoppingInfo.isEmpty()) {
            LOGGER.error("the shopping list info is empty,please check!");
            return 0.0F;
        }

        LOGGER.info("Begin to compute shopping list");

        String[] infoAry = shoppingInfo.split("\n");

        // 因为第一行有可能为空，有可能为促销信息，所以要特殊处理一下
        List<PromotionMessage> promotionList = dealFirstLineInfo(infoAry[0]);
        int infoAryLength = infoAry.length;
        List<Product> productList = new ArrayList<>();
        List<Coupon> couponList = new ArrayList<>();
        LocalDate settleDate = LocalDate.now();
        for (int i = 1; i < infoAryLength; i++) {
            String lineStr = infoAry[i];
            if (!lineStr.isEmpty()) {
                if (lineStr.contains("\\ |")) {
                    // 如果包含 |，那么就为促销信息
                    Optional<PromotionMessage> message = dealPromotion(lineStr);
                    message.ifPresent(promotionList::add);
                } else if (lineStr.contains("*") && lineStr.contains(":")) {
                    // 如果包含 * 和 :，那么就为商品信息
                    productList.add(parseProductInfo(lineStr));
                } else if (lineStr.split(" ").length > 1) {
                    // 如果字符串长度大于1，那么就是优惠券信息了
                    couponList.add(dealCouponInfo(lineStr));
                } else {
                    // 最后剩下的就是结算日期，因为只有一个结算日期
                    settleDate = DateUtil.parseStrToDate(lineStr.trim());
                }
            }
        }

        ShoppingList shoppingList = new ShoppingList(promotionList, productList, settleDate, couponList);
        return computeShoppingPrice(shoppingList);
    }

    /**
     * 计算购物车价格
     *
     * @param shoppingList 购物车列表
     * @return 购物车价格
     */
    private static float computeShoppingPrice(ShoppingList shoppingList) {
        if (shoppingList.getPromotionMessageList().isEmpty()) {
            LOGGER.info("No promotion information found, all items will be calculated at the original price");
        } else {
            LOGGER.info("Find the promotion information. If the dates match, it will be calculated according to the promotion price");

            // 设置商品促销信息
            List<Product> setPromotionList = setProductPromotion(shoppingList.getPromotionMessageList(),
                    shoppingList.getProductList(), shoppingList.getSettleDate());
            shoppingList.setProductList(setPromotionList);
        }

        List<Product> productList = shoppingList.getProductList();

        // 计算结果
        float sum = 0.0F;
        for (Product product : productList) {
            sum += product.getProductNum() * product.getProductPrice() * product.getPromotion();
        }

        return computeCouponInfo(sum, shoppingList.getCouponList(), shoppingList.getSettleDate());
    }

    /**
     * 计算商品优惠券信息
     *
     * @param sumPrice   总价
     * @param couponList 优惠券信息
     * @param settleDate 结算日期
     * @return 计算完商品优惠券信息后的价格
     */
    private static float computeCouponInfo(float sumPrice, List<Coupon> couponList, LocalDate settleDate) {
        if (couponList.isEmpty()) {
            LOGGER.info("Coupon information not found");
            return sumPrice;
        } else {
            for (Coupon coupon : couponList) {
                LocalDate couponDate = coupon.getCouponDate();
                int couponThd = coupon.getCouponThd();
                int couponPrice = coupon.getCouponPrice();

                if (couponDate.isAfter(settleDate) && (int) sumPrice >= couponThd) {
                    sumPrice -= couponPrice;
                }
            }
        }

        // 四舍五入，保留2位小数
        BigDecimal bigDecimal = new BigDecimal(sumPrice);
        return bigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();
    }

    private static List<Product> setProductPromotion(List<PromotionMessage> promotionMessageList,
                                                     List<Product> productList, LocalDate settleDate) {
        for (PromotionMessage message : promotionMessageList) {
            float promoteRadio = message.getPromoteRadio();
            LocalDate promoteDate = message.getPromoteDate();
            String promoteProductType = message.getPromoteProductType();

            if (promoteDate.equals(settleDate)) {
                productList.stream().filter(item -> item.getProductType().equals(promoteProductType)).
                        forEach(item -> item.setPromotion(promoteRadio));
            }
        }

        return productList;
    }

    private static Coupon dealCouponInfo(String couponStr) {
        String[] couponAry = couponStr.split(" ");
        Coupon coupon = new Coupon();
        coupon.setCouponDate(DateUtil.parseStrToDate(couponAry[0].trim()));
        coupon.setCouponThd(Integer.parseInt(couponAry[1].trim()));
        coupon.setCouponPrice(Integer.parseInt(couponAry[2].trim()));

        return coupon;
    }

    private static List<PromotionMessage> dealFirstLineInfo(String firstLine) {
        List<PromotionMessage> promotionMessageList = new ArrayList<>();
        if (firstLine.isEmpty()) {
            LOGGER.info("Has not found promotion message.");
            return new ArrayList<>();
        } else {
            Optional<PromotionMessage> message = dealPromotion(firstLine);
            message.ifPresent(promotionMessageList::add);
        }
        return promotionMessageList;
    }

    private static Optional<PromotionMessage> dealPromotion(String firstLine) {
        String[] promotionStr = firstLine.split("\\|");
        PromotionMessage promotion = new PromotionMessage();
        // 因为促销信息格式为 时间 | 比例 | 商品类型，所以分隔后长度一定为3
        if (promotionStr.length != 3) {
            LOGGER.error("promotion message has error,please check");
            return Optional.empty();
        } else {
            LocalDate localDate = DateUtil.parseStrToDate(promotionStr[0].trim());
            promotion.setPromoteDate(localDate);
            promotion.setPromoteRadio(Float.parseFloat(promotionStr[1]));
            promotion.setPromoteProductType(promotionStr[2].trim());
        }

        return Optional.of(promotion);
    }

    private static Product parseProductInfo(String productStr) {
        String[] firstSplit = productStr.split("\\*");
        Product product = new Product();
        product.setProductNum(Integer.parseInt(firstSplit[0].trim()));

        String[] secondSplit = firstSplit[1].split(":");
        String productName = secondSplit[0].trim();

        // 根据名称查询类型
        Optional<Product> findResult = PRODUCTS.stream().filter(item -> item.getProductName().equals(productName)).findFirst();

        if (findResult.isPresent()) {
            Product item = findResult.get();
            product.setPromotion(item.getPromotion());
            product.setProductType(item.getProductType());
        }

        product.setProductName(productName);
        product.setProductPrice(Float.parseFloat(secondSplit[1].trim()));

        return product;
    }
}
